/**
 * 重新封装数据库操作方法
 * 简化各种操作
 * 后续的数据库操作都改用该文件的封装
 * 封装地址：https://blog.csdn.net/tuzi007a/article/details/128075919
 */

// 因为操作数据库最耗时的是连接数据库，所以对数据库进行封装，解决重复连接数据库问题
// 简单封装后存在多个实例化重复调用数据连接的问题，所以在封装时要解决。
const { MongoClient, ObjectId } = require('mongodb'); // 引入数据库
const config_db = require('./config'); // 引入db配置文件

/**
 * 封装db库
 */

class Db {
  // 创建一个静态方法，解决多个实例重复连接数据库的问题
  // 比如实例testDb1已经连接过数据库了，
  // 但是实例testDb2仍然会调用connect方法 去连接数据库，浪费了性能
  // 我们需要的是，当前面有实例连接过数据库时， 
  // 数据库处于连接状态，那么以后的实例都不需要再去连接了
  static getInstance() {
    if (!Db.instance) { // 如果不存在实例
      Db.instance = new Db(); // 就创建实例
    }
    return Db.instance;
  }
  constructor() {
    // 设置一个属性 解决某个实例上多个方法重复调用数据库连接的问题
    // 比如实例testDb已经连接过数据库了，那么在用find查询时，就不要再去重复连接了
    this.dbClient = '';

    this.connect(); // 初始化的时候就连接数据库
  }

  connect() { // 连接数据库
    return new Promise((resolve, reject) => {
      if (!this.dbClient) { // 如果dbClient不存在，就说明没调用过
        MongoClient.connect(config_db.dbUrl, (err, client) => {
          if (err) {
            reject(err);
          } else {
            this.dbClient = client.db(config_db.dbName);
            resolve(this.dbClient);
          }
        })
      } else { // 如果已经存在 说明被调用过了
        return resolve(this.dbClient);
      }
    })
  }

  // 获取_id，因为查询时用到的_id的值是ObjectId()类型的数据
  getObjectId(id) {
    return new ObjectId(id);
  }

  // 增：向集合内添加一个文档
  addOne(collectionName, datas) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .insertOne(datas, (err, data) => {
              if (err) {
                reject(err);
              } else {
                resolve(data);
              }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 增：向集合内添加多个文档
  addMany(collectionName, dataArr) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .insertMany(dataArr, (err, data) => {
            if (err) {
              reject(err);
            } else {
              resolve(data);
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 删：删除一条文档
  deleteOne(collectionName, query) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .deleteOne(query, (err, data) => {
            if(err) {
              reject(err);
            }else {
              resolve(data);
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 删：删除多条文档
  deleteMany(collectionName, query) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .deleteMany(query, (err, data) => {
            if(err) {
              reject(err);
            }else {
              resolve(data);
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 删：清空集合内所有文档
  clear(collectionName) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .deleteMany((err, data) => {
            if(err) {
              reject(err);
            }else {
              resolve(data);
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 查：根据文档中的字段查找一条或多条符合条件的文档
  find(collectionName, query) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .find(query)
            .toArray((err, data) => {
              if(err) {
                reject(err);
              } else {
                resolve(data);
              }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 查：查询某个集合内的所有文档
  findAll(collectionName) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .find()
            .toArray((err, data) => {
              if(err) {
                reject(err);
              } else {
                resolve(data);
              }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 增：向文档内增加某个字段
  addKey(collectionName, query, datas) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(query, { $set: datas }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 删：删除文档中某个字段
  delteKey(collectionName, query, datas) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(query, { $unset: datas }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 改：修改单个文档中某个字段的值
  updateKey(collectionName, query, datas) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(query, { $set: datas }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 改：修改多个文档中某个字段的值
  updateManyKey(collectionName, query, datas) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateMany(query, { $set: datas }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 增：向数组内添加值
  addDeep(collectionName, query, datas) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(query, { $addToSet: datas }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 删：删除数组内的某个值
  deleteDeep(collectionName, query, datas) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(query, { $pull: datas }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 增：嵌套增加对象中的字段
  addDeepKey(collectionName, query, datas) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(query, { $set: datas }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 删：删除嵌套数据中的某个字段
  deleteDeepKey(collectionName, query, datas) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(query, { $unset: datas }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 改：修改嵌套数据中的某个字段的值
  updateDeepKey(collectionName, query, datas) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .updateOne(query, { $set: datas }, (err, data) => {
            if (err) {
              reject(err)
            } else {
              resolve(data)
            }
          })
        })
      .catch(err => reject(err))
    })
  }

  // 查：多条件查询
  findCriteria(collectionName, queryArr) {
    return new Promise((resolve, reject) => {
      this.connect()
        .then(db => {
          db.collection(collectionName)
            .aggregate(queryArr)
            .toArray((err, data) => {
                if(err) {
                  reject(err);
                } else {
                  resolve(data);
                }
              })
        })
      .catch(err => reject(err))
    })
  }

}
module.exports = Db.getInstance();
